// 存放上树之后的dom的数组
let instance = [];
// 每个上树之后的dom元素的 高度 之和
let upPageDomHeightSum = 0;

// 触发提示的入口方法
function FontMessage(options) {
  upPageDomHeightSum = 0;
  // 获取body根元素
  let body = document.body;
  // 创建一个信息dom
  let dom = document.createElement("div");
  dom.style.position = "relative";
  dom.setAttribute("class", "tips-plus");

  // 判断颜色
  const colorJudge = (options) => {
    const typeArr = [
      {
        type: "info",
        borderColor: "#ebeef5",
        backgroundColor: "#edf2fc",
        color: "#909399",
        icon: "i",
      },
      {
        type: "success",
        borderColor: "#e1f3d8",
        backgroundColor: "#f0f9eb",
        color: "#67c23a",
        icon: "√",
      },
      {
        type: "warning",
        borderColor: "#faecd8",
        backgroundColor: "#fdf6ec",
        color: "#e6a23c",
        icon: "!",
      },
      {
        type: "error",
        borderColor: "#fde2e2",
        backgroundColor: "#fef0f0",
        color: "#f56c6c",
        icon: "×",
      },
    ];
    typeArr.find((item) => item.type == (options.type ? options.type : "info"));
    return typeArr.find(
      (item) => item.type == (options.type ? options.type : "info")
    );
  };
  // 添加图标和 message信息
  dom.innerHTML =
    `<b style="position:absolute;left: 22px;
    top: 50%;transform: translateY(-50%);color:#fff;width:16px;font-size: 12px;
    line-height: 16px;
    text-align: center;display: inline-block;height:17px;border-radius:50%;background:${
      colorJudge(options).color
    }">${colorJudge(options).icon}</b>` +
    (options.message ? options.message : "我是提示组件哦");

  //设置样式集合
  dom.style.cssText = `
    min-width: 300px;
    max-width: 600px;
    display: inline-block;
    background: ${colorJudge(options).backgroundColor};
    position: fixed;
    padding: 15px 30px 15px 60px;
    left: 50%;
    border-radius: 4px;
    opacity:0.3;
    border: 1px solid ${colorJudge(options).borderColor};
    font-size: 14px;
    transition: all .2s linear;
    transform: translateX(-50%);
    color: ${colorJudge(options).color};
    z-index: 9999;
  `;

  // 设置上树之后的dom元素的 高度 之和
  upPageDomHeightSum = instance.reduce((prev, next) => {
    return (prev == 0 ? 0 : prev) + next.offsetHeight;
  }, 0);
  instance.push(dom);
  // 上树
  body.appendChild(dom);
  // 设置每次添加进来的dom的top值
  dom.style.top =
    upPageDomHeightSum - dom.offsetHeight + instance.length * 16 + "px";

  // 设置两次为了动画(需要插值才会触发)
  dom.style.top = upPageDomHeightSum + instance.length * 16 + "px";
  dom.style.opacity = 1;

  // 每隔指定时间 清除第一个dom   剩余dom的top减去每次减去的dom高度
  setTimeout(
    () => {
      if (instance[0]) {
        instance[0].style.top = 0 + "px";
        instance[0].style.opacity = 0;
      }
      setTimeout(() => {
        // 删除上树的数组的第一个元素
        let r = instance.shift();
        // 将其它dom的top减去删除的dom的高度
        for (let i = 0; i < instance.length; i++) {
          instance[i].style.top =
            parseInt(instance[i].style.top) - r.offsetHeight - 16 + "px";
        }
        // 删除dom元素
        if (document.body.contains(dom)) document.body.removeChild(dom);
      }, 201);
    },
    options.duration ? options.duration : 2000
  );
}


// RAF  ---   定时器
class Interval {
  #timerId = null
  #loop = null
  #count = 0
  #timer = Date.now()
  #return = false
  // 构造器
  constructor(cb, time = 0) {
    if(typeof (time * 1) !== 'number') {
      cb(new Error('抱歉! 您的时间参数必须为 数字类型 或者 字符串数字类型'))
      return
    }
    this.#loop = () => {
      if(Date.now() > this.#timer + time * (this.#count + 1)){
        cb(this.#count)
        this.#count++
      }
      if(this.#return) return // 如果标识为true 就是停止定时器
      this.#timerId = requestAnimationFrame(this.#loop)
    }
    this.#timerId = requestAnimationFrame(this.#loop)
  }
  // 清除定时器
  clear() {
    this.#return = true // 改变标识为 true
    cancelAnimationFrame(this.#timerId) // 清除 requestAnimationFrame
  }
}

// 帅气的图片加载工具
export default class HandsomeImageLoading {
  #wrap = null // 渲染图片的容器
  #wrapId = ''
  #wrapWidth = 0
  #wrapHeight = 0
  #imgWidth = 0
  #imgHeight = 0
  #imgList = [] // 图片列表
  #fragment = document.createDocumentFragment()
  #imgOverArr = []
  #time = 3 // 延迟加载倍率 3 --> 0~2000
  #innerWrap = null
  #maxTime = []
  #index = 0
  constructor(wrap, imgList = []) {
    if(imgList.length > 2000) {
      FontMessage({ type: 'warning', message: '您的图片太多了, 建议 2000 条之内，效果会更好! 已经自动帮您截取了 2000 条', duration: 3000 })
      imgList = imgList.slice(0, 2000)
    }
    let T = this
    T.#wrap = wrap
    T.#wrapWidth = wrap.offsetWidth
    T.#wrapHeight = wrap.offsetHeight
    T.#imgList = imgList
    if(!T.#isDraw(T)) return
    T.#create_wrap_inner(T)
    T.#wrapStyle(T)
  }

  // 创建wrap内容器
  #create_wrap_inner = (T) => {
    T.#innerWrap = document.createElement('div')
    T.#innerWrap.id = 'handsome-image-inner'
    T.#innerWrap.style.cssText = `
    width: 100%;
    height: 100%;
    transform-style: preserve-3d;
    perspective: 800px;
    `
  }

  #isDraw = (T) => {
    let res = true
    if(!T.#wrap) {
      FontMessage({ type: 'warning', message: '请确定 HandsomeImageLoading 构造函数的第一个参数是否为图片的容器' })
      res = false
      return
    }
    if(T.#imgList.length === 0) {
      FontMessage({ type: 'warning', message: '请确定 HandsomeImageLoading 构造函数的第二个参数图片地址列表为空！无法展示特效' })
      res = false
      return
    }
    return res
  }

  // 开始加载
  start = async(time) => {
    let T = this
    if(!T.#isDraw(T)) return
    T.#time = time <= 1 ? 1 : time
    if(!T.#imgWidth)  T.#imgWidth = (T.#wrapWidth / 10)
    if(!T.#imgHeight) T.#imgHeight = (T.#wrapWidth / 10) / 2
    T.#index = T.#computed_show(T) * 9
    T.#imgList.forEach((item, key) => {
      let img = new Image()
      img.src = item
      img.draggable = ''
      img.style.cssText = `
      width: ${ T.#wrapWidth / 10 }px;
      max-height: ${ (T.#wrapWidth / 10) / 2 }px;
      border: 2px solid #fff;
      cursor: pointer;
      margin-top: 4px;
      position: relative;
      `
      if(T.#index !== 0 && key <= T.#index) {
        img.style.transform = 'translateZ(1000px)'
      }
      T.#imgOverArr.push(img)
      T.#fragment.appendChild(img)
    })
    // 所有图片加载完毕，再上树
    await Promise.all(T.#imgOverArr.map((img) => new Promise(resolve => img.addEventListener('load',()=> resolve(img))))).then(res => {
      res.forEach((item, index) => {
        if(index + 1 <= T.#computed_show(T) * 9) {
          T.#random_show(item, T)
        }
      })
      T.#innerWrap.appendChild(T.#fragment)
      T.#wrap.appendChild(T.#innerWrap)
      T.#compute_margin(T)
    })
  }

  // 计算容器宽度 和 每个img宽度，设置margin
  #compute_margin = (T) => {
    let wrap_width = document.getElementById(T.#wrapId).offsetWidth
    let imgWidth = 0
    let space = 0
    let newWrap_width = 0
    let ob = new ResizeObserver(entries => {
      if(!imgWidth) imgWidth = T.#imgOverArr[0].offsetWidth
      newWrap_width = entries[0].contentRect.width
      space = Math.floor(wrap_width / imgWidth)
      let width = (newWrap_width -  imgWidth * space) / (space + 1)
      T.#imgOverArr.forEach((value, index) => {
        value.style.marginLeft = `${ width }px`  
      })
    })
    ob.observe(T.#wrap)
  }

  // 设置每个图片的随机展示
  #random_show = (item, T) => {
    let num = 1000
    let timeout = null
    let timer = null
    let randomNum = (Math.random() * T.#time).toFixed(3) * 1
    T.#maxTime.push(randomNum)
    timeout = setTimeout(() => {
      timer = new Interval((val) => {
        if(num === 0) {
          timer.clear()
          T.#maxTime.shift()
          // 所有动画都完毕的钩子
          if(T.#maxTime.length == 0) {
            T.#wrap.style.overflow = 'auto';
          }
        }
        item.style['z-index'] = Math.floor(randomNum * 10) // z-index越久越大
        item.style.transform = `translateZ(${ num }px)`;
        num -= 10
      }, 10)
      clearTimeout(timeout)
    }, randomNum * 1000)
  }

  // 计算有多少个图片需要加载动画
  #computed_show = (T) => {
    return Math.ceil(T.#wrap.offsetHeight / (T.#imgHeight + 4 + 4))
  }

  #wrapStyle = (T) => {
    T.#wrap.setAttribute('id', 'handsome-image-loading')
    T.#wrapId = 'handsome-image-loading'
    T.#wrap.style.cssText = `
    overflow: hidden;
    width: 100%;
    height: 100%;
    `
  }
}